#include <Windows.h>
#include <stdint.h>
#include <stdio.h>
#include <comdef.h>

#include "IHelloWorld.hpp"
#include "CMyComClassFactory.hpp"
#include "CMyComClass.hpp"
#include "DllServer.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction

CMyComClassFactory::CMyComClassFactory() {
    RefCount = 0;
    gdwDllLockCount++;
}

CMyComClassFactory::~CMyComClassFactory() {
    gdwDllLockCount--;
}


//////////////////////////////////////////////////////////////////////
// IUnknown methods

STDMETHODIMP_(ULONG) CMyComClassFactory::AddRef()
{
    return ++RefCount;               // Increment this object's reference count.
}

STDMETHODIMP_(ULONG) CMyComClassFactory::Release()
{
    ULONG uRet = --RefCount;             // Decrement this object's reference count.

    if (0 == RefCount)             // Releasing last reference?
        delete this;

    return uRet;
}

STDMETHODIMP CMyComClassFactory::QueryInterface(REFIID riid, void** ppv)
{
    HRESULT hrRet = S_OK;
    OLECHAR* guidString;
    StringFromCLSID(riid, &guidString);

    printf("... CMyComClassFactory::QueryInterface called on IID %ws\r\n", guidString);
    ::CoTaskMemFree(guidString);
    // Check that ppv really points to a void*.

    if (IsBadWritePtr(ppv, sizeof(void*)))
        return E_POINTER;

    // Standard QI initialization - set *ppv to NULL.

    *ppv = NULL;

    // If the client is requesting an interface we support, set *ppv.

    if (InlineIsEqualGUID(riid, IID_IUnknown))
    {
        *ppv = (IUnknown*)this;
    }
    else if (InlineIsEqualGUID(riid, IID_IClassFactory))
    {
        *ppv = (IClassFactory*)this;
    }
    else
    {
        hrRet = E_NOINTERFACE;
    }

    // If we're returning an interface pointer, AddRef() it.

    if (S_OK == hrRet)
    {
        ((IUnknown*)*ppv)->AddRef();
    }

    return hrRet;
}


//////////////////////////////////////////////////////////////////////
// IClassFactory methods

STDMETHODIMP CMyComClassFactory::CreateInstance(IUnknown* pUnkOuter,
    REFIID    riid,
    void** ppv)
{
    HRESULT hrRet;
    CMyComClass* pMyComClass;

    // We don't support aggregation, so pUnkOuter must be NULL.

    if (NULL != pUnkOuter)
        return CLASS_E_NOAGGREGATION;

    // Check that ppv really points to a void*.

    if (IsBadWritePtr(ppv, sizeof(void*)))
        return E_POINTER;

    *ppv = NULL;

    // Create a new COM object!

    pMyComClass = new CMyComClass;

    if (NULL == pMyComClass)
        return E_OUTOFMEMORY;

    // QI the object for the interface the client is requesting.

    hrRet = pMyComClass->QueryInterface(riid, ppv);

    // If the QI failed, delete the COM object since the client isn't able
    // to use it (the client doesn't have any interface pointers on the object).

    if (FAILED(hrRet)) {
        printf("... QueryInterface from class factory failed (error 0x%08x)\r\n", hrRet);
        delete pMyComClass;
    }

    return hrRet;
}

STDMETHODIMP CMyComClassFactory::LockServer(BOOL fLock)
{
    // Increase/decrease the DLL ref count, according to the fLock param.

    fLock ? gdwDllLockCount++ : gdwDllLockCount--;

    return S_OK;
}
